#include <linux/hashtable.h> // hashtable API
#include <linux/module.h>    // module_{init,exit}, MODULE_*
#include <linux/string.h>    // strcpy, strcmp
#include <linux/types.h>     // u32 etc.


DECLARE_HASHTABLE(acl_tbl, 4);

struct client_info {
	int pid;
	int gid;
	int client_id;
	int op;
};

struct h_node {
	int key;
	struct client_info *client;
	struct hlist_node node;
};

static u32 myhash(const int client_id) {
	u32 key = 0;
	int i;
	for (i = 0; i < 4; i++) {
		key += (client_id >> (i * 8)) & 0xff;
	}
	return key;
}

#define MAX_CLIENT 100
struct h_node **g_nodes = NULL;
static void init_table(void) 
{
	int i, key;
	unsigned bkt;
	struct h_node *cur;
	if (g_nodes != NULL) {
		return;
	}
	printk("Current PID is  %d\n", current->pid);
	hash_init(acl_tbl);

	//struct client_info *clients[MAX_CLIENT];
	//struct h_node *nodes[MAX_CLIENT];
	g_nodes = kmalloc(sizeof(struct h_node *) * MAX_CLIENT, GFP_KERNEL);
	
	for (i = 0; i < MAX_CLIENT; i++) {
		struct client_info *client = kmalloc(sizeof (struct client_info), GFP_KERNEL);
		g_nodes[i] = kmalloc(sizeof (struct h_node), GFP_KERNEL);
		client->client_id = i;
		client->pid = 100 + i;
		client->op = 1;
		g_nodes[i]->key = i;
		g_nodes[i]->client = client;

		key = myhash(i);
		hash_add_rcu(acl_tbl, &(g_nodes[i]->node), key);
	}

	hash_for_each_rcu(acl_tbl, bkt, cur, node) {
		struct client_info *client = cur->client;
		pr_info("myhashtable: element: client_id = %d, key = %d\n", myhash(client->client_id), client->client_id);
	}
}

static inline int check_valid_client(int client_id) {
	struct h_node *cur;
	int key = myhash(client_id);
	hash_for_each_possible_rcu(acl_tbl, cur, node, key) {
		struct client_info *client = cur->client;
		pr_info("myhashtable: match for key %u: data = %d, name = %d\n", key, cur->key, client->pid);

		// cur->node->next

		if (client_id != client->client_id) {
			pr_info("myhashtable: element key %d not found! get: %d\n", client_id, client->client_id);
			return 0;
		}
		return client->op == 1;
	}
	return 0;
}

static int __init myhashtable_init(void)
{
    pr_info("myhashtable: module loaded\n");

	init_table();

	check_valid_client(5);
	check_valid_client(23);
	check_valid_client(24);

    return 0;
}

static void __exit myhashtable_exit(void)
{
    // Do nothing (needed to be able to unload the module).
    pr_info("myhashtable: module unloaded\n");
}


module_init(myhashtable_init);
module_exit(myhashtable_exit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Silly kernel hashtable API example module.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");